﻿using Grow.Db.Repository;
using Grow.EventBus.Cap.Enum;
using Grow.EventBus.Cap.SubscriberEventData;
using Grow.EventBus.Cap.SubscriberHandler;
using Grow.Module.Service;
using Grow.Scheduler.Job;

namespace Grow.EventBus.Cap.Job
{
    public class RetrySubscriberEventDataJob : JobBase
    {

        private readonly EventBusOptions _eventBusOptions;
        private readonly ISubscriberHandler _subscriberHandler;
        private readonly IRepository<SubscriberEventDataEntity> _subscriberEventDataRepository;
        public RetrySubscriberEventDataJob(IServiceContext serviceContext) : base(serviceContext)
        {
            _eventBusOptions = serviceContext.GetOptions<EventBusOptions>();
            _subscriberHandler = _serviceContext.GetRequiredService<ISubscriberHandler>();
            _subscriberEventDataRepository = serviceContext.GetRequiredService<IRepository<SubscriberEventDataEntity>>();

        }

        public override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            var eventDataDtos = await GetSubscriberEventDataOfNeedRetryAsync(cancellationToken);
            if (eventDataDtos.Count == 0)
            {
                return;
            }
            Parallel.ForEach(eventDataDtos, eventDataDto =>
            {
                _ = Task.Factory.StartNew(async () =>
                {
                    await _subscriberHandler.ExecuteAsync(eventDataDto, cancellationToken);
                }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
            });
        }

        private async Task<List<SubscriberEventDataDto>> GetSubscriberEventDataOfNeedRetryAsync(CancellationToken cancellationToken)
        {
            var now = DateTimeOffset.Now;
            var sendAt = now.AddSeconds(-_eventBusOptions.StartRetryAfter);

            var serviceContext = _serviceContext.GetServiceContext();

            var subscriberEventDataEntitys= await _subscriberEventDataRepository.GetSelectRepository().AsNoTracking().Where(a =>
                a.SendAt < sendAt
                && a.EnvironmentName == _eventBusOptions.EnvironmentName
                && a.RetryCount < _eventBusOptions.RetryLimitCount
                && (a.LockEndTime < now || a.LockEndTime == null)
                && (a.EventDataStatus == EventDataStatusEnum.Wait || a.EventDataStatus == EventDataStatusEnum.Fail)
               ).Take(_eventBusOptions.RetryFailedMax)
               .ToListAsync(cancellationToken);

            return subscriberEventDataEntitys.Select(a => new SubscriberEventDataDto(
                       a.Id,
                       a.EventName,
                       a.EventHandlerName,
                       a.EventDataType,
                       a.EventHandlerType,
                       a.EventBody,
                       a.EventHeads,
                       a.SendAt,
                       a.DelayAt
               )).ToList();
        }
    }
}
